package com.xuanheng.utils.http.async;


import java.io.BufferedReader;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * A Basic HTTP Request, All other requests should extend this
 *
 * @author Nikki
 */
public abstract class HttpRequest implements Closeable {

    /**
     * The URL String. We aren't using URL objects just so it's easier to
     * manipulate in GET requests, though it'd be a good idea to allow a URL
     * constructor.
     */
    protected String url;

    private boolean can302=true;

    /**
     * The UserAgent for this request
     */
    private String userAgent;

    /**
     * The connection instance
     */
    protected HttpURLConnection connection;

    /**
     * A map of header elements. This could be done with guava if we wanted to
     * add another dependency...
     */
    private Map<String, List<Object>> headers = new HashMap<>();

    /**
     * A list of request parameters (RequestData is a simple wrapper which
     * allows chaining)
     */
    protected RequestParams parameters;

    private InputStream inputStream = null;
    OutputStream outputStream = null;

    public void setConnectTimeout(int connectTimeout) {
        if (connectTimeout > 0) {
            this.connectTimeout = connectTimeout;
        }
    }
public void setCan302(boolean can){
    can302=can;
}
    private int connectTimeout = 15000;


    /**
     * Construct a new HttpRequest from the specified url string
     *
     * @param url The URL
     */
    public HttpRequest(String url) {
        this.url = url;
    }

    /**
     * Called automatically when using getConnection/getResponse* to execute the
     * request
     *
     * @throws IOException If an error occurred while executing
     */
    public abstract void execute() throws IOException;

    /**
     * Opens the connection and sets the uesr agent, headers, etc.
     *
     * @throws MalformedURLException
     * @throws IOException           If an error occurred while opening the connection
     */
    protected void openConnection() throws MalformedURLException, IOException,
            SocketTimeoutException {
        connection = (HttpURLConnection) new URL(url).openConnection();
        connection.setInstanceFollowRedirects(false);
        connection.setConnectTimeout(connectTimeout);
        connection.setInstanceFollowRedirects(can302);
        connection.setReadTimeout(connectTimeout);
        connection.setUseCaches(false);
        if (userAgent != null) {
            connection.setRequestProperty("User-Agent", userAgent);
        }

        if (!headers.isEmpty()) {
            for (Map.Entry<String, List<Object>> e : headers.entrySet()) {
                for (Object o : e.getValue()) {
                    connection.addRequestProperty(e.getKey(), o.toString());
                }
            }
        }
    }

    /**
     * Get the connection object. This is useless except when called after
     * execute or getResponse*
     *
     * @return The connection object
     */
    public HttpURLConnection getConnection() {
        return connection;
    }

    /**
     * Get the HTTP response code. This will execute the request if it hasn't
     * already.
     *
     * @return The response code from the connection
     * @throws IOException If an error occurred while executing the request.
     */
    public int getResponseCode() throws MalformedURLException, IOException,
            SocketTimeoutException {
        checkConnection();
        return connection.getResponseCode();
    }

    /**
     * Gets the HTTP response as an InputStream
     *
     * @return The response stream
     */
    public InputStream getResponseStream() throws MalformedURLException, IOException,
            SocketTimeoutException {
        checkConnection();
        return connection.getInputStream();
    }

    /**
     * Get the HTTP response body as a string. This will execute the request if
     * it hasn't already. TODO: Do we want to make sure there's no ending \n?
     *
     * @return The response string
     * @throws IOException If an error occurred while executing the request.
     */
    public String getResponseBody() throws MalformedURLException, IOException,
            SocketTimeoutException {
        checkConnection();
        inputStream = connection.getInputStream();
        StringBuilder builder = new StringBuilder();
        BufferedReader reader = new BufferedReader(new InputStreamReader(
                inputStream));
        String line;
        while ((line = reader.readLine()) != null) {
            builder.append(line).append('\n');
        }
        return builder.toString();
    }

    /**
     * Gets a header from the response
     *
     * @param name The request header name
     * @return The request header
     */
    public String getResponseHeader(String name) throws MalformedURLException, IOException,
            SocketTimeoutException {
        checkConnection();
        return connection.getHeaderField(name);
    }

    /**
     * Checks if the connection has been initialized, and if not initialize and
     * execute the request.
     *
     * @throws IOException If an error occurred while executing the request
     */
    private void checkConnection() throws MalformedURLException, IOException,
            SocketTimeoutException {
        if (connection == null) {
            execute();
        }
    }

    /**
     * Set the parameter object
     *
     * @param parameters The new parameter object
     */
    public void setParameters(RequestParams parameters) {
        this.parameters = parameters;
    }

    /**
     * Add an http request header to this request
     *
     * @param name  The request header name
     * @param value The request header value
     */
    public void addHeader(String name, Object value) {
        List<Object> list = headers.get(name);
        if (list == null) {
            headers.put(name, list = new LinkedList<>());
        }
        list.add(value);
    }

    /**
     * Set the HTTP User agent
     *
     * @param userAgent The user agent
     */
    public void setUserAgent(String userAgent) {
        this.userAgent = userAgent;
    }

    public void setHeaders(Map<String, List<Object>> headers) {
        this.headers = headers;
    }

    /**
     * Close the connection (used in AutoCloseable)
     */
    @Override
    public void close() {
        // TODO Auto-generated method stub
        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (Throwable ignored) {
            }
            inputStream = null;
        }
        if (outputStream != null) {
            try {
                outputStream.close();
            } catch (Throwable ignored) {
            }
            outputStream = null;
        }
        if (connection != null) {
            try {
                connection.disconnect();
            } catch (Throwable ignored) {
            }

            connection = null;
        }
    }
}
